/******************************************************************************\
    PluginDataBase.h discribes a database of plugins.
    It's used with acm server. See main.cpp for details.
    Copyright (C) 2008  Victor Vinogradov (fly3333@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
\******************************************************************************/

#pragma once

#include ".\BaseDataBase.hpp"
#include <Common\Plugin.h>

namespace nsPluginDataBase
{
	static const char MAIN_PROC_NAME[] = "?MainProc@@YG_NHKPAX@Z";

	const unsigned long FLAG_ENABLED = 0x00000001ul;

	const size_t MAX_NAME_LENGTH = 255;

	struct sPluginData0
	{
		unsigned long Key;
		char Name[ MAX_NAME_LENGTH + 1 ];
		bool IsEnabled;
		HMODULE Handle;
		tMainProc MainProc;
	};

	struct sPluginData1
	{
		unsigned long Key;
		char Name[ MAX_NAME_LENGTH + 1 ];
		unsigned long Flags;
		HMODULE Handle;
		tMainProc MainProc;
	};

	typedef sPluginData1 sPluginData;

	void ConversionFunction0( void *new_data, const void *prev_data );

	const sConversionTable ConversionTable[] = 
	{
		{ sizeof( sPluginData0 ), sizeof( sPluginData1 ), &ConversionFunction0 }
	};

	struct NameCompare : public std::binary_function< cDataPointer< sPluginData >, cDataPointer< sPluginData >, bool >
	{
		bool operator()( const cDataPointer< sPluginData > &left, const cDataPointer< sPluginData > &right ) const
		{
			return _stricmp( left->Name, right->Name ) < 0;
		}
	};

	typedef cBaseDataBase< sPluginData >::KeyIterator KeyIterator;
	typedef cPublicIterator< sPluginData, NameCompare > NameIterator;

	class cWardPluginDataBase : public cBaseDataBase< sPluginData >
	{
	private:
		std::set< cDataPointer< sPluginData >, NameCompare > NameIndex;
	protected:
		virtual bool Indexed( const cDataPointer< sPluginData > &data_pointer )
		{
			return NameIndex.find( data_pointer ) != NameIndex.end();
		}
		virtual void InsertIndex( const cDataPointer< sPluginData > &data_pointer )
		{
			NameIndex.insert( data_pointer );
		}
		virtual void UpdateIndex( const cDataPointer< sPluginData > &data_pointer, unsigned long index )
		{
			NameIndex.find( data_pointer )->Index = index;
		}
		virtual void DeleteIndex( const cDataPointer< sPluginData > &data_pointer )
		{
			NameIndex.erase( data_pointer );
		}
		virtual void ClearIndex()
		{
			NameIndex.clear();
		}
	public:
		cWardPluginDataBase( /*bool auto_update*/ )
			: cBaseDataBase( /*auto_update*/ )
		{
		}
		NameIterator BeginByName() const
		{
			return NameIterator( NameIndex.begin() );
		}
		NameIterator FindByName( const char name[] ) const
		{
			if( strlen( name ) > MAX_NAME_LENGTH )
				return EndByName();
			sPluginData plugin_data;
			strcpy( plugin_data.Name, name );
			return NameIterator( NameIndex.find( cDataPointer< sPluginData >( &plugin_data ) ) );
		}
		NameIterator EndByName() const
		{
			return NameIterator( NameIndex.end() );
		}
		bool GetByName( const char name[], sPluginData &plugin_data )
		{
			cLocker locker( *this );
			NameIterator iterator = FindByName( name );
			if( iterator == EndByName() )
				return false;
			plugin_data = *iterator;
			return true;
		}
	};

	class cPluginDataBase : public cPointerObject< cWardPluginDataBase >
	{
	public:
		bool Create( /*bool auto_update = false*/ )
		{
			cWardPluginDataBase *ward_plugin_data_base;
			try
			{
				ward_plugin_data_base = new cWardPluginDataBase( /*auto_update*/ );
			}
			catch( ... )
			{
				return false;
			}
			if( !SwitchToWardObject( ward_plugin_data_base ) )
			{
				delete ward_plugin_data_base;
				return false;
			}
			return true;
		}
		void Destroy()
		{
			SwitchToSubObject( NULL );
		}
	};
}